package server.main;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;

import common.MessageHandle;
import common.MessageHandle.MessageType;
import common.User;

public class ServerThreadHandle extends Thread {
	private Socket socket = null;
	private static ArrayList<ServerThreadHandle> threadForUsers = new ArrayList<>();

	private static MessageHandle message;
	private ObjectInputStream ois; // Read input object
	private ObjectOutputStream oos; // Write output object
	private Object obj;
	private User user;
	private int threadID;

	public ServerThreadHandle(Socket socket, int count) throws IOException {
		super(String.valueOf(count));

		threadID = count;

		// Initialize server components
		this.socket = socket;
		ois = new ObjectInputStream(socket.getInputStream());
		oos = new ObjectOutputStream(socket.getOutputStream());

		// Read initial information
		try {
			obj = ois.readObject();
			message = (MessageHandle) obj;

			user = new User(message.getOwner(), threadID);
			// Inform available client that a new user online
			System.out.println(user + " is online now!");

			// Add new thread for user
			threadForUsers.add(this);

			// Send list of online members
			sendListOnline();
		}catch (ClassNotFoundException e1) {
			System.err.println("Read initial information failed!");
		}
	}

	private void sendListOnline() throws IOException {
		message = new MessageHandle("Server");
		message.setMsType(MessageType.SYSTEM);

		Iterator<ServerThreadHandle> iter = threadForUsers.iterator();
		ArrayList<User> listOnline = new ArrayList<>();
		while (iter.hasNext()) {
			listOnline.add(iter.next().user);
		}

		message.setListOnline(listOnline);
		message.setMessage(String.valueOf(threadID));

		sendOutObject(threadForUsers, false);
	}

	@Override
	public void run() {
		try {
			// Wait for a message from client and send it through corresponding IDs
			while ((obj = ois.readObject()) != null) {
				message = (MessageHandle) obj;
				routeMessage();
			}

			oos.close();
			ois.close();
			socket.close();
		}catch (IOException | ClassNotFoundException e) {
			// System.err.println("An user with thread ID = " + this.getName() + " has just left!");
			// TODO Finalize: Inform the available users that this user was offline
			// Remove a thread for user
			threadForUsers.remove(this);

			// Update list online
			try {
				sendListOnline();
				socket.close();
				oos.close();
				ois.close();
			}catch (IOException e1) {
				System.err.println("An user with thread ID = " + this.getName() + " has just left!");
			}
		}
	}

	private synchronized void routeMessage() throws IOException {
		switch (message.getMsType()) {
			case PRIVATE:
				// Navigate message follow message type and target client ID
				sendOutObject(threadForUsers, true);
				break;
			case SYSTEM:
				// TODO process system message
				break;
			case GLOBAL:
				sendOutObject(threadForUsers, false);
				break;
			default:
				break;
		}
	}

	/**
	 * Sent out message
	 * 
	 * @param message
	 * @param threadForUsers
	 * @param isFilter
	 *            = True for private message
	 * @throws IOException
	 */
	private synchronized void sendOutObject(ArrayList<ServerThreadHandle> threadForUsers, boolean isFilter) throws IOException {
		if(message != null) {
			Iterator<ServerThreadHandle> iter = threadForUsers.iterator();
			ServerThreadHandle t = null;
			while (iter.hasNext()) {
				t = iter.next();

				if((isFilter & (t.threadID == message.getTargetClientID())) || !isFilter) {
					t.oos.writeObject(message); // Send out the message
					t.oos.flush();
				}
			}

			message = null;
		}
	}
}
